#!/bin/sh
# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

#
# BEGIN COMMON INVENTORY CODE
#

# Reason for this no-op: shellcheck disable=... before the first command disables the error for the
# entire script.
:

# Disable unused variable error (needed to keep track of version)
# shellcheck disable=SC2034
CMK_VERSION="2.5.0b1"

_load_config() {
    # defaults:
    INVENTORY_INTERVAL=$((3600 * 4))

    [ -r "${1}" ] || return
    # shellcheck source=../cfg_examples/mk_inventory.cfg
    . "${1}"
}

inpath() {
    # replace "if type [somecmd]" idiom
    # 'command -v' tends to be more robust vs 'which' and 'type' based tests
    command -v "${1:?No command to test}" >/dev/null 2>&1
}

_get_epoch() {
    # This is taken from the check_mk_agent. Try to keep it in sync with set_up_get_epoch...

    # On some systems date +%s returns a literal %s
    if date +%s | grep "^[0-9].*$" >/dev/null 2>&1; then
        date +%s
    else
        # do not check whether perl is even present.
        # in weird cases we may be fine without get_epoch.
        perl -e 'print($^T."\n");'
    fi
}

_insert_persist_option() {
    # insert the persist(...) option, if it is not already present.
    sed -e '/^<<<.*\(:persist(\).*>>>/{p;d;}' -e 's/^<<<\([^>]*\)>>>$/<<<\1:persist('"${1}"')>>>/'
}

run_persisted() {
    INTERVAL="${1}"
    shift

    FLAGFILE="${MK_VARDIR}/persisted/${1}.${REMOTE}"
    NOW="$(_get_epoch)"
    UNTIL=$((NOW + INTERVAL + 600))
    LAST_RUN="$(cat "${FLAGFILE}" 2>/dev/null)" || LAST_RUN=0

    if "${MK_FORCE_INVENTORY:-false}"; then
        "$@" | _insert_persist_option "${UNTIL}"
        return
    fi

    if [ $((NOW - LAST_RUN)) -lt "${INTERVAL}" ]; then
        return
    fi

    mkdir "${FLAGFILE%/*}"
    echo "${NOW}" >"${FLAGFILE}"

    "$@" | _insert_persist_option "${UNTIL}"
}

#
# END COMMON INVENTORY CODE
#

_data_items() {
    #  size                 must be multiplied with block size (512byte)
    #  uuid
    #  device/address       can be bus address, e.g. 0000:21:00.0
    #  device/firmware_rev
    #  device/model         can be "SAMSUNG MZVLB512HAJQ-00000", "LOGICAL VOLUME"
    #  device/serial
    #  device/type          can be "0", "MMC", "SD"
    #  device/vendor        can be "ATA  ", "HP  ", "QEMU "
    # Excluded data_items
    #  dev
    #  wwid                 world wide ID
    #  nguid
    #  device/dev           what is second `dev` for?
    #  device/name          found at some mmc devices
    #  device/wwid          world wide ID?
    cat <<HERE
    size
    uuid
    device/address
    device/firmware_rev
    device/model
    device/serial
    device/type
    device/vendor
HERE
}

_distro_files() {
    cat <<HERE
    /etc/oracle-release
    /etc/debian_version
    /etc/gentoo-release
    /etc/lsb-release
    /etc/redhat-release
    /etc/SuSE-release
    /etc/os-release
    /usr/share/cma/version
HERE
}

sections_mk_inventory() {

    section_lnx_packages

    section_lnx_distro

    section_lnx_cpuinfo

    section_dmidecode

    section_lnx_uname

    section_lnx_video

    section_lnx_ip_r

    section_lnx_sysctl

    section_lnx_block_devices
}

section_lnx_packages() {
    # List of DEB packages
    if inpath dpkg-query; then
        echo "<<<lnx_packages:sep(124)>>>"
        dpkg-query --show --showformat='${Package}|${Version}|${Architecture}|deb|-|${Summary}|${Status}\n'
    fi

    # List of RPM packages in same format
    if inpath rpm; then
        echo "<<<lnx_packages:sep(9)>>>"
        rpm -qa --qf '%{NAME}\t%{VERSION}\t%{ARCH}\trpm\t%{RELEASE}\t%{SUMMARY}\t-\n'
    fi

    # List Gentoo packages
    if inpath equery; then
        echo "<<<lnx_packages:sep(124)>>>"
        equery -C list --format "\$category/\$name|\$fullversion|\$mask2|ebuild|Repository \$repo|installed" \* | head -n -1
    fi
}

section_lnx_distro() {
    # Information about distribution
    echo "<<<lnx_distro:sep(124)>>>"
    _distro_files | while read -r f; do
        [ -e "$f" ] || continue
        echo "[[[$f]]]"
        tr \\n \| <"$f" | sed 's/|$//'
        echo
    done
}

section_lnx_cpuinfo() {
    # CPU Information. We need just the first one
    [ -e /proc/cpuinfo ] || return
    echo "<<<lnx_cpuinfo:sep(58)>>>"
    sed 's/[[:space:]]*:[[:space:]]*/:/' </proc/cpuinfo
}

section_dmidecode() {
    # Information about main board, memory, etc.
    inpath dmidecode || return
    echo "<<<dmidecode:sep(58)>>>"
    dmidecode -q | sed 's/\t/:/g'
}

section_lnx_uname() {
    # Information about kernel architecture
    inpath uname || return
    echo "<<<lnx_uname>>>"
    uname -m
    uname -r
}

section_lnx_video() {
    # Collect VGAs if they are present
    vgas="$(lspci | grep VGA | cut -d" " -f 1)"
    [ -n "$vgas" ] || return
    echo "<<<lnx_video:sep(58)>>>"
    printf '%s\n' "$vgas" | while IFS= read -r vga; do
        lspci -v -s "$vga"
    done
}

section_lnx_ip_r() {
    # Some networking information
    inpath ip || return
    echo "<<<lnx_ip_r>>>"
    ip r
    ip -6 r
}

section_lnx_sysctl() {
    # Kernel configuration
    inpath sysctl || return
    echo "<<<lnx_sysctl>>>"
    sysctl -ae | grep -v -e '^net\.ipv4\.tcp_fastopen_key' \
        -e '^net\.ipv6\.conf\..*\.stable_secret'
}

section_lnx_block_devices() {
    # The subshell below is for scoping UEVENT_FILE and DEVICE_DIR.
    (
        # use sep=0 rather than 124 in order to be more flexible in the parse function
        echo "<<<lnx_block_devices:sep(0)>>>"
        find "/sys/block/" -maxdepth 1 -type f -o -type l | while read -r DEVICE; do
            UEVENT_FILE="$(realpath "${DEVICE}")/uevent"
            DEVICE_DIR="$(dirname "${UEVENT_FILE}")"

            grep "DEVTYPE=disk" -q "${UEVENT_FILE}" || continue
            [ "${UEVENT_FILE#*"devices/virtual/"}" = "${UEVENT_FILE}" ] || continue

            # use pipe as delimiter - also to mark begin of lines for consitency check
            echo "|device|${DEVICE_DIR}|"

            _data_items | while read -r item; do
                [ -f "$DEVICE_DIR/${item}" ] && echo "|${item}|$(cat "$DEVICE_DIR/${item}")|"
            done
        done 2>/dev/null
    )
}

#
# BEGIN COMMON INVENTORY CODE
#

main() {
    _load_config "${MK_CONFDIR}/mk_inventory.cfg"

    run_persisted "${INVENTORY_INTERVAL:?}" sections_mk_inventory
}

[ -z "${MK_SOURCE_AGENT}" ] && main
